package org.umlviewer.xmiexporter;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.umlviewer.model.DiagramElement;
import org.umlviewer.model.Relationship;
import org.umlviewer.model.UmlDiagram;
import org.umlviewer.model.diagram_class.Clase;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.Namespace;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.umlviewer.model.sequence_diagram.OccurrenceSpecification;

/**
 *Exportar los diagramas a XMI
 * @author Rafa
 */
public class XmiExporter {

    private static final Logger logger =  Logger.getLogger(XmiExporter.class.getName());
    
    public XmiExporter(){
        
    }
    
    //TODO: el nodo documentation, y el extension
    public static void exportFile(UmlDiagram diagram, String filepath) throws IOException{
        Document xmidoc = new Document();        
        Namespace nsxmi = Namespace.getNamespace("xmi", "http://schema.omg.org/spec/XMI/2.1");
        Namespace nsuml = Namespace.getNamespace("uml", "http://schema.omg.org/spec/UML/2.1");
        Element root = new Element("XMI", "xmi", "http://schema.omg.org/spec/XMI/2.1");        
        root.addNamespaceDeclaration(nsuml);
        root.addNamespaceDeclaration(nsxmi);
        root.setAttribute("version","2.1",nsxmi);
        //nodo documentation
        Element documentation = new Element("Documentation",nsxmi);
        documentation.setAttribute("exporter","Enterprise Architect");
        documentation.setAttribute("exporterVersion","6.5");
        Element model = new Element("Model",nsuml);
        model.setAttribute("type","uml:Model",nsxmi).setAttribute("name","Model").setAttribute("visibility","public");
        //el paquete donde va incluido todo el modelo
        Element pack = new Element("packagedElement");
        pack.setAttribute("type","uml:Package",nsxmi).setAttribute("visibility","public");
        //para almacenar las coordenandas de los elementos
        Element positions = new Element("elements");
        String geom = "";
        
        ArrayList<DiagramElement> elements = diagram.getElements();
        for(DiagramElement e : elements){
            //se obtiene la representacion en xmi del elmento
            Element aux = e.xmiSerialize();
            
            //las coordenadas del elemtno en pantalla
            Element coord = new Element("element");
            coord.setAttribute("subject",e.getId());
            geom = "Left="+e.getPosX()+";Top="+e.getPosY()+";Right="+e.getWidth()+";Bottom="+e.getHeight()+";";
            coord.setAttribute("geometry",geom);
            positions.addContent(coord);
            
            //para la generalizacion, por ahora para las clases
            //if(e instanceof Clase){
                ArrayList<Relationship> rels = diagram.getRelationshipsFrom(e);
                ArrayList<RelationExporter> rexp = new ArrayList<RelationExporter>();
                for(Relationship r : rels){
                    if(r.getType() == Relationship.GENERALIZATION){
                        Element gen = new Element("generalization");
                        gen.setAttribute("type","uml:Generalization",nsxmi);
                        gen.setAttribute("id",r.getId(),nsxmi); 
                        gen.setAttribute("general",r.getDestination().getId());
                        aux.addContent(gen);
                    }
                    //verificar asociaciones
                    else if(r.getType() == Relationship.ASSOCIATION ){
                        rexp.add( new AssociationExporter(r, 1) );
                    }                    
                    else if(r.getType() == Relationship.REALITATION){
                        rexp.add(new RealizationExporter(r,1));
                    }
                    else if(r.getType() == Relationship.AGGREGATION || r.getType() == Relationship.COMPOSITION){
                        Element asos = new Element("ownedAttribute");
                        asos.setAttribute("type","uml:Property",nsxmi);
                        asos.setAttribute("id",r.getId(),nsxmi);
                        //se necesita una referencia a la asociacion
                        RelationExporter relExporter = new AssociationExporter(r, 2);
                        asos.setAttribute("association",relExporter.getId());
                        if(r.getType() == Relationship.ASSOCIATION)
                            asos.setAttribute("aggregation","none"); 
                        if(r.getType() == Relationship.AGGREGATION)
                            asos.setAttribute("aggregation","shared"); 
                        if(r.getType() == Relationship.COMPOSITION)
                            asos.setAttribute("aggregation","composite"); 
                        asos.addContent(new Element("type").setAttribute("idref",r.getDestination().getId(),nsxmi));
                        aux.addContent(asos);
                        rexp.add(relExporter);
                    }
                    else if(r.getType() == Relationship.INCLUDE){
                        Element inc = new Element("include");
                        inc.setAttribute("type","uml:Include",nsxmi);
                        inc.setAttribute("id",r.getId(),nsxmi);
                        inc.setAttribute("visibility","public");
                        inc.setAttribute("addition",r.getDestination().getId());
                        aux.addContent(inc);
                    }
                    else if(r.getType() == Relationship.EXTEND){
                        Element inc = new Element("extend");
                        inc.setAttribute("type","uml:Extend",nsxmi);
                        inc.setAttribute("id",r.getId(),nsxmi);
                        inc.setAttribute("visibility","public");
                        inc.setAttribute("extendedCase",r.getDestination().getId());
                        aux.addContent(inc);
                    }
                    //
                    else if(r.getType() == Relationship.MESSAGE){
                        OccurrenceSpecification src = new OccurrenceSpecification(r.getSource().getId());
                        OccurrenceSpecification dst = new OccurrenceSpecification(r.getDestination().getId());
                        pack.addContent(src.xmiSerialize());
                        pack.addContent(dst.xmiSerialize());
                        Element mesg = new Element("message");
                        mesg.setAttribute("type","uml:Message",nsxmi);
                        mesg.setAttribute("id",r.getId(),nsxmi);
                        mesg.setAttribute("name", r.getLabel());
                        mesg.setAttribute("messageKind", "complete");
                        mesg.setAttribute("messageSort", "synchCall");
                        mesg.setAttribute("sendEvent",src.getId());
                        mesg.setAttribute("receiveEvent",dst.getId());
                        pack.addContent(mesg);
                    }
                }
            //}
            if(aux!=null)
                pack.addContent(aux);
            //se exportan las relaciones
            if(!rexp.isEmpty()){
                for(RelationExporter rx : rexp){
                    pack.addContent(rx.xmiSerialize());
                }
            }
            rexp.clear();
        }
                
        //nodo extension
        Element extension = new Element("Extension",nsxmi);
        extension.setAttribute("extender","Enterprise Architect");
        extension.setAttribute("extenderID","6.5");
        extension.setContent(new Element("diagrams").setContent(new Element("diagram").setContent(positions)));        
        
        model.setContent(pack);
        root.addContent(documentation);
        root.addContent(model);
        root.addContent(extension);
        xmidoc.setRootElement(root);
        
        //se guarda el archivo
        XMLOutputter xmlOutputter = new XMLOutputter(Format.getPrettyFormat());
        if(filepath.lastIndexOf(".")<0)
            filepath+=".xmi";
        xmlOutputter.output(xmidoc, new FileOutputStream(filepath));
        logger.log(Level.INFO, "Exportacion completada");
    }
    
}
